#include "HelloWindow.h"
#include <QExposeEvent>
#include <QResizeEvent>
#include <QKeyEvent>
#include <QCoreApplication>
#include <QOpenGLVertexArrayObject>

const char *vertexShaderSource = R"(#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
})";

const char *fragmentShaderSource = R"(#version 330 core
out vec4 FragColor;
void main()
{
   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
})";

HelloWindow::HelloWindow()
    : QWindow(),
      m_context(nullptr),
      m_update_pending(false),
      m_vao(nullptr),
      m_vbo(nullptr)
{
    setSurfaceType(QWindow::OpenGLSurface);
    resize(800, 600);
    setTitle("LearnOpenGL");
}

HelloWindow::~HelloWindow()
{
    delete m_vbo;
}

void HelloWindow::renderNow()
{
    if (!isExposed())
        return;

    if (!m_context) {
        m_context = new QOpenGLContext(this);
        m_context->setFormat(requestedFormat());
        m_context->create();

        m_context->makeCurrent(this);

        initializeOpenGLFunctions();

        bool result;
        m_program = new QOpenGLShaderProgram(this);
        result = m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
        if (!result) { qDebug("Vertex error: %s", qPrintable(m_program->log())); }
        result = m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
        if (!result) { qDebug("Fragment error: %s", qPrintable(m_program->log())); }
        result = m_program->link();
        if (!result) { qDebug("Link error: %s", qPrintable(m_program->log())); }


        GLfloat vertices[] {
            -0.5f, -0.5f, 0.0f, // left
             0.5f, -0.5f, 0.0f, // right
             0.0f,  0.5f, 0.0f  // top
        };

        m_vao = new QOpenGLVertexArrayObject(this);
        m_vbo = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);

        m_vao->create();
        m_vbo->create();

        m_vao->bind();

        m_vbo->bind();
        m_vbo->setUsagePattern(QOpenGLBuffer::StaticDraw);
        m_vbo->allocate(vertices, sizeof(vertices));

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), (GLvoid *)nullptr);
        glEnableVertexAttribArray(0);

        m_vbo->release();
        m_vao->release();

        auto retinaScale = devicePixelRatio();
        glViewport(0, 0,
                   width() * retinaScale,
                   height() * retinaScale);
    }
    else {
        m_context->makeCurrent(this);
    }

    //render
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    m_program->bind();

    m_vao->bind();
    glDrawArrays(GL_TRIANGLES, 0, 3);
    m_vao->release();

    m_program->release();


    m_context->swapBuffers(this);
    renderLater();
}

void HelloWindow::renderLater()
{
    if (!m_update_pending) {
        m_update_pending = true;
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}

bool HelloWindow::event(QEvent *event) {
    switch (event->type()) {
    case QEvent::UpdateRequest:
        m_update_pending = false;
        renderNow();
        return true;
    default:
        return QWindow::event(event);
    }
}

void HelloWindow::exposeEvent(QExposeEvent *event)
{
    renderNow();
    return QWindow::exposeEvent(event);
}

void HelloWindow::resizeEvent(QResizeEvent *event)
{
    const auto &size = event->size();
    auto retinaScale = devicePixelRatio();
    if (m_context)
        glViewport(0, 0,
                   size.width() * retinaScale,
                   size.height() * retinaScale);
    renderNow();

    return QWindow::resizeEvent(event);
}

void HelloWindow::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_Escape:
        close();
        return;
    default:
        return QWindow::keyPressEvent(event);
    }
}
